#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string>
#include <unistd.h>
enum
{
    SOCK_CREATE_ERROR = 1,
    SOCK_BIND_ERROR,
    SOCK_LISTEN_ERROR,
    EPOLL_CREATE_ERROR,
    EPOLL_CTL_ERROR
};
class EpollServer
{
    static const int gbacklog = 16;
    static const int gsize = 128;
    static const int events_size = 64;
    static const int defaultfd = -1;

public:
    EpollServer(uint16_t port) : _listensock(-1), _port(port)
    {
        for (int i = 0; i < events_size; ++i)
        {
            _events[i].data.fd = defaultfd;
            _events[i].events = 0;
        }
    }
    void InitServer()
    {
        // 创建套接字
        _listensock = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            std::cerr << "Create Socket Error!" << std::endl;
            exit(SOCK_CREATE_ERROR);
        }
        // 绑定套接字
        sockaddr_in addr;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        int n = ::bind(_listensock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
        if (n < 0)
        {
            std::cerr << "Socket Bind Error!" << std::endl;
            exit(SOCK_BIND_ERROR);
        }
        // 设置套接字为监听状态
        n = ::listen(_listensock, gbacklog);
        if (n < 0)
        {
            std::cerr << "Socket Listen Error!" << std::endl;
            exit(SOCK_LISTEN_ERROR);
        }
        // 创建epoll模型
        _epfd = ::epoll_create(gsize);
        if (_epfd < 0)
        {
            std::cerr << "Epoll Create Error!" << std::endl;
            exit(EPOLL_CREATE_ERROR);
        }
        // 添加listen套接字(读事件)进入epoll模型
        struct epoll_event ep;
        ep.data.fd = _listensock;
        ep.events = EPOLLIN;
        n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ep);
        if (n < 0)
        {
            std::cerr << "Epoll Ctl Error" << std::endl;
            exit(EPOLL_CTL_ERROR);
        }
    }
    void AcceptClient()
    {
        int sockfd = ::accept(_listensock, nullptr, nullptr);
        if (sockfd < 0)
        {
            std::cerr << "Accept Error!" << std::endl;
            return;
        }
        // 新的套接字添加进epoll模型
        epoll_event ep;
        ep.data.fd = sockfd;
        ep.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ep);
        if (n < 0)
        {
            std::cerr << "Add Sockfd To Epoll Error!" << std::endl;
            return;
        }
    }
    void ServiceIO(int sockfd)
    {
        char inbuffer[1024];
        int n = recv(sockfd, inbuffer, sizeof(inbuffer), 0);
        if (n > 0)
        {
            std::string outbuffer;
            outbuffer += "[client say#] ";
            outbuffer += inbuffer;
            send(sockfd, outbuffer.c_str(), 1024, 0);
        }
        else if(n == 0)
        {
            //对端连接关闭
            std::cout << "client closed done" << std::endl;
            ::close(sockfd);
            epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);
            return;
        }
        else 
        {
            std::cerr << "recv error!" << std::endl;
        }
    }
    void Start()
    {
        while (true)
        {
            int n = ::epoll_wait(_epfd, _events, events_size, -1);
            if (n < 0)
            {
                std::cerr << "Epoll Wait Error!" << std::endl;
                continue;
            }
            else if (n == 0)
            {
                std::cout << "Timeout..." << std::endl;
                continue;
            }
            else
            {
                // 成功等待了文件描述符
                for (size_t i = 0; i < events_size; ++i)
                {
                    if (_events[i].data.fd != defaultfd)
                    {
                        if (_events[i].data.fd == _listensock)
                        {
                            AcceptClient();
                        }
                        else
                        {
                            ServiceIO(_events[i].data.fd);
                        }
                    }
                }
            }
        }
    }
    ~EpollServer()
    {
    }

private:
    int _listensock;
    uint16_t _port;
    struct epoll_event _events[events_size];
    int _epfd;
};